package com.sheng.leetcode.year2022.month06.day30;

import org.junit.Test;

/**
 * @author liusheng
 * @date 2022/06/30
 *
 * 1175. 质数排列
 *
 * 请你帮忙给从 1 到 n 的数设计排列方案，使得所有的「质数」都应该被放在「质数索引」（索引从 1 开始）上；你需要返回可能的方案总数。
 * 让我们一起来回顾一下「质数」：质数一定是大于 1 的，并且不能用两个小于它的正整数的乘积来表示。
 * 由于答案可能会很大，所以请你返回答案 模 mod 10^9 + 7 之后的结果即可。
 *
 * 示例 1：
 * 输入：n = 5
 * 输出：12
 * 解释：举个例子，[1,2,5,4,3] 是一个有效的排列，但 [5,2,3,4,1] 不是，因为在第二种情况里质数 5 被错误地放在索引为 1 的位置上。
 *
 * 示例 2：
 * 输入：n = 100
 * 输出：682289015
 *
 * 提示：
 * 1 <= n <= 100
 *
 * 来源：力扣（LeetCode）
 * 链接：https://leetcode.cn/problems/prime-arrangements
 * 著作权归领扣网络所有。商业转载请联系官方授权，非商业转载请注明出处。
 */
public class LeetCode1175 {

    @Test
    public void test01(){
//        int n = 5;
        int n = 100;
        System.out.println(new Solution().numPrimeArrangements(n));
    }

}
class Solution {
    public int numPrimeArrangements(int n) {
        //给1-n的数设计排序规则，n = 5时，即对[1,2,3,4,5]进行排序，10^9 * 7
        //质数的排序次数，乘以非质数的排序次数即可
        //计算出质数的数量
        boolean isPrime;
        int z = 0;
        for(int i = 2 ;i <= n; i++){
            isPrime = true;
            for(int j = 2; j < i; j++){
                if(i % j == 0){
                    isPrime = false;
                    break;
                }
            }
            if(isPrime){
                z++;
            }
        }
        long a = 1;
        for (int i = n - z; i > 1; i--) {
            a = a * i % 1000000007;
        }
        for (int i = z; i > 1; i--) {
            a = a * i % 1000000007;
        }
        return (int) a;
    }
}

//根据题意，可将问题转换为求 n 以内的质数个数，记为 a，同时可得非质数个数为 b = n - a。
//
//质数的放置方案数为 a!，而非质数的放置方案数为 b!，根据「乘法原理」总的放置方案数为 a! * b!。
//
//我们可以通过「打表」的方式将 100 以内的质数预处理到数组 list 中，对于每个 n 而言，我们找到第一个满足「值小于等于 n」的位置，从而得知 n 范围以内的质数个数。
//
//作者：AC_OIer
//链接：https://leetcode.cn/problems/prime-arrangements/solution/by-ac_oier-t3lk/
//来源：力扣（LeetCode）
//著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。
//class Solution {
//    static int MOD = (int)1e9+7;
//    static List<Integer> list = new ArrayList<>();
//    static {
//        for (int i = 2; i <= 100; i++) {
//            boolean ok = true;
//            for (int j = 2; j * j <= i; j++) {
//                if (i % j == 0) ok = false;
//            }
//            if (ok) {
//                list.add(i);
//            }
//        }
//    }
//    public int numPrimeArrangements(int n) {
//        int l = 0, r = list.size() - 1;
//        while (l < r) {
//            int mid = l + r + 1 >> 1;
//            if (list.get(mid) <= n) l = mid;
//            else r = mid - 1;
//        }
//        int a = r + 1, b = n - a;
//        long ans = 1;
//        for (int i = b; i > 1; i--) {
//            ans = ans * i % MOD ;
//        }
//        for (int i = a; i > 1; i--) {
//            ans = ans * i % MOD ;
//        }
//        return (int)ans;
//    }
//}
//
//更进一步，对于特定的 n 而言，我们在预处理 100 以内的质数时，已经可以确定在 [1, n] 内有多少个质数，从而省掉二分操作。
//使用数组 cnts 记录下不超过当前值范围内质数的个数，cnts[i] = x 含义为在 [1, i] 范围内质数数量为 x。
//
//class Solution {
//    static int MOD = (int)1e9+7;
//    static int[] cnts = new int[110];
//    static {
//        List<Integer> list = new ArrayList<>();
//        for (int i = 2; i <= 100; i++) {
//            boolean ok = true;
//            for (int j = 2; j * j <= i; j++) {
//                if (i % j == 0) ok = false;
//            }
//            if (ok) list.add(i);
//            cnts[i] = list.size();
//        }
//    }
//    public int numPrimeArrangements(int n) {
//        int a = cnts[n], b = n - a;
//        long ans = 1;
//        for (int i = b; i > 1; i--) ans = ans * i % MOD ;
//        for (int i = a; i > 1; i--) ans = ans * i % MOD ;
//        return (int)ans;
//    }
//}
//
//作者：AC_OIer
//链接：https://leetcode.cn/problems/prime-arrangements/solution/by-ac_oier-t3lk/
//来源：力扣（LeetCode）
//著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。
